#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

#define CHARSIZE 1024
#define ARGC 64

#define SEP " "

int split(char* command, char* argv[])
{
  assert(command);
  assert(argv);
  
  argv[0] = strtok(command, SEP);

  int i = 1;

  while((argv[i++] = strtok(NULL, SEP)));


//int i = 0;
  //while(i)
  //{
  //  argv[i] = strtok(NULL, SEP);//传参NULL表示继续拆分这个字符串
  //  if(argv[i] == NULL) break;
  //  ++i;
  //}
  
  return 0;

}

void debug(char* const argv[])
{
  for(int i = 0; argv[i]; i++)
  {
    printf("argv[%d]:%s\n", i, argv[i]);
  }
}

int main()
{
  char myenv[32][256];
  int index_env = 0;
  extern char** environ;
  int exit_num = 0;
  
  while(1)
  {
    char command[CHARSIZE] = {  };
    char* argv[ARGC] = { NULL };
    printf("[root@Linux shell]# ");
    fflush(stdout);
    
    //等待输入命令
    char* ref = fgets(command, sizeof(command), stdin);//会读入字符： \n
    assert(ref);
    (void)ref;//release版本下assert会去除，ref变量创建没有被使用会报警告，这个操作是为了去警告

    //printf("%s", ref);
    command[strlen(command) - 1] = '\0';//去\n,fgets会读入\n
    //sleep(1);
    
    //拆分命令选项
    int n = split(command, argv);
    if(n != 0) continue; //拆分异常直接跳过此循环
  
    //debug(argv);
    
    //命令细节处理
    if(strcmp(argv[0], "ls") == 0)
    {
      int pos = 0;
      while(argv[pos]) pos++;
      argv[pos++] = (char*)"--color=auto";//实现不同文件的显示不同颜色
      argv[pos] = NULL;
    }
    else if(strcmp(argv[0], "cd") == 0)//cd更改目录位置应该由父进程执行
    {
      if(argv[1] != NULL) chdir(argv[1]); //更改当前所处的目录路径
      continue;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
      if(argv[1] != NULL)
      {
        //创建新的环境变量前需要先将其存储起来
        strcpy(myenv[index_env], argv[1]);
        putenv(myenv[index_env++]);//将环境变量添加到环境变量表中
      }
      continue;
    }
    else if(strcmp(argv[0], "env") == 0)
    {
      //环境变量表由shell父进程中显示
      for(int i = 0; environ[i]; i++)
      {
        printf("%d:%s\n", i, environ[i]);
      }
      continue;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
      const char* targer_env = NULL;
      //echo $xxx 显示环境变量内容
      if(*argv[1] == '$')
      {
        if(argv[1][1] == '?')
        {
          printf("%d\n", exit_num);//退出码
          continue;
        }
        else targer_env = argv[1] + 1;//+1表示过滤$符号

        if(targer_env != NULL) printf("%s=%s\n",targer_env, getenv(targer_env));
      }
      continue;
    }

    pid_t id = fork();
    assert(id >= 0); 

    if(id == 0)
    {
      //child
      execvp(argv[0], argv); 
      exit(0);
    }

    //parent
    int status = 0;
    int ret = waitpid(id, &status, 0);//阻塞等待
    if(ret > 0)//子进程等待成功
    {
      exit_num = WEXITSTATUS(status);//获取进程退出码
    }
  }

  return 0;
}
